DLO-JZ Optimisation de l'apprentissage - Jour 1¶

Optimisation système d'une boucle d'apprentissage Resnet-50.

car

Objet du notebook¶

Le but de ce notebook est d'optimiser un code d'apprentissage d'un modèle Resnet-50 sur Imagenet pour Jean Zay en implémentant :

  • TP 1 : l'accélération GPU
  • TP 2 : l'Automatic Mixed Precision
  • TP 3 : le Channels Last Memory Format

Les cellules dans ce notebook ne sont pas prévues pour être modifiées, sauf rares exceptions indiquées dans les commentaires. Les TP se feront en modifiant le code dlojz.py.

Les directives de modification seront marquées par l'étiquette TODO :, dans le notebook suivant.

Les solutions sont présentes dans le répertoire solutions.

Notebook rédigé par l'équipe assistance IA de l'IDRIS, juin 2023


Environnement de calcul¶

Un module PyTorch doit avoir été chargé pour le bon fonctionnement de ce Notebook. Nécessairement, le module pytorch-gpu/py3/1.11.0 :

In [1]:
!module list
Currently Loaded Modulefiles:
 1) cuda/11.7.1                7) magma/2.7.0-cuda       
 2) nccl/2.12.12-1-cuda        8) sox/14.4.2             
 3) cudnn/8.5.0.96-11.7-cuda   9) sparsehash/2.0.3       
 4) gcc/8.5.0(8.3.1:8.4.1)    10) libjpeg-turbo/2.1.3    
 5) openmpi/4.1.1-cuda        11) pytorch-gpu/py3/2.0.0  
 6) intel-mkl/2020.4          
>

Les fonctions python de gestion de queue SLURM dévelopées par l'IDRIS et les fonctions dédiées à la formation DLO-JZ sont à importer.

Le module d'environnement pour les jobs et la taille des images sont fixés pour ce notebook.

TODO : choisir un pseudonyme (maximum 5 caractères) pour vous différencier dans la queue SLURM et dans les outils collaboratifs pendant la formation et la compétition.

In [2]:
from idr_pytools import display_slurm_queue, gpu_jobs_submitter, search_log
from dlojz_tools import controle_technique, compare, GPU_underthehood, plot_accuracy, lrfind_plot, imagenet_starter
MODULE = 'pytorch-gpu/py3/1.11.0'
image_size = 176
account = 'for@v100'
name = 'pseudo'   ## Pseudonyme à choisir

Creation d'un repertoire checkpoints si cela n'a pas déjà été fait.

In [3]:
!mkdir checkpoints

Gestion de la queue SLURM¶

Cette partie permet d'afficher et de gérer la queue SLURM.

Pour afficher toute la queue utilisateur :

In [4]:
display_slurm_queue(name)
 Done!

Remarque: Cette fonction utilisée plusieurs fois dans ce notebook permet d'afficher la queue de manière dynamique, rafraichie toutes les 5 secondes. Cependant elle ne s'arrête que lorsque la queue est vide. Si vous désirez reprendre la main sur le notebook, il vous suffira d'arrêter manuellement la cellule avec le bouton stop. Cela a bien sûr aucun impact sur le scheduler SLURM. Les jobs ne seront pas arrêtés.

Si vous voulez arrêter des jobs dans la queue:

  • Annuler tous vos jobs dans la queue (décommenter la ligne suivante) : !scancel -u $USER
  • Annuler un job dans votre queue (décommenter la ligne suivante et ajouter le numéro du job à la fin de la ligne)
In [5]:
#!scancel -u $USER

Debug¶

Cette partie debug permet d'afficher les fichiers de sortie et les fichiers d'erreur du job.

Il est nécessaire dans la cellule suivante (en décommentant) d'indiquer le jobid correspondant sous le format suivant.

*Remarque* : dans ce notebook, lorsque vous soumettrez un job, vous recevrez en retour le numéro du job dans le format suivant : jobid = ['123456']. La cellule ci-dessous peut ainsi être facilement actualisée."

In [6]:
jobid = ['1493206']

Fichier de sortie :

In [7]:
%cat {search_log(contains=jobid[0])[0]}
>>> Training on  1  nodes and  1  processes
model: Resnet-50
number of parameters: 25557032
global batch size: 128 - mini batch size: 128
Optimizer: SGD (
Parameter Group 0
    dampening: 0
    lr: 0.1
    maximize: False
    momentum: 0.9
    nesterov: False
    weight_decay: 0.0
)
DATALOADER 10 True True True 3 False 
image batch shape : torch.Size([128, 3, 176, 176])
>>> Training complete in: 0:05:53.561484
>>> Training performance time: min 4.501930236816406 avg 4.766597509384155 seconds (+/- 0.14579007426206964)
>>> Loading performance time: min 0.00013065338134765625 avg 0.00015664100646972656 seconds (+/- 1.434471526857817e-05)
>>> Forward performance time: 1.8760071092722368 seconds (+/- 0.09052225345171229)
>>> Backward performance time: 2.901484557560512 seconds (+/- 0.08457200485922346)
>>> Peak Power during training: 62.22 W)
>>> Validation time estimation: 0:17:49.429900
>>> Sortie trace #####################################
>>>JSON {"GPU process - Forward/Backward": [6.2423787117004395, 5.0220746994018555, 4.957103490829468, 5.0125415325164795, 4.943894863128662, 4.966577529907227, 5.019651651382446, 4.99901819229126, 4.964149236679077, 4.885366678237915, 4.904692649841309, 4.781780242919922, 4.951511859893799, 4.84772253036499, 4.766597509384155, 4.684057712554932, 4.787169456481934, 4.795409679412842, 4.917107105255127, 4.681482791900635, 4.62136173248291, 4.676070213317871, 4.717148303985596, 4.702852964401245, 4.64032506942749, 4.7434868812561035, 4.692547082901001, 4.687806129455566, 4.651872873306274, 4.681207180023193, 4.635786294937134, 4.501930236816406, 4.82633376121521, 4.589520454406738, 4.554682970046997, 4.706907749176025, 4.620109558105469, 4.52482008934021, 4.774549961090088, 4.610376834869385, 4.6381542682647705, 4.710811138153076, 5.109354257583618, 4.724546909332275, 4.806366920471191, 4.937008380889893, 4.881795883178711, 4.729339838027954, 4.814107179641724, 4.803519010543823], "CPU process - Dataloader": [3.979201555252075, 0.0001895427703857422, 0.00014591217041015625, 0.00017380714416503906, 0.00013375282287597656, 0.00013637542724609375, 0.0001621246337890625, 0.0001347064971923828, 0.00015163421630859375, 0.00015854835510253906, 0.00017547607421875, 0.0001685619354248047, 0.00014591217041015625, 0.0001819133758544922, 0.00013637542724609375, 0.0001373291015625, 0.0001327991485595703, 0.00013065338134765625, 0.0001361370086669922, 0.00015592575073242188, 0.0001666545867919922, 0.0001628398895263672, 0.00017642974853515625, 0.00017499923706054688, 0.0001685619354248047, 0.00014543533325195312, 0.00013685226440429688, 0.00013947486877441406, 0.00013518333435058594, 0.00015878677368164062, 0.00015592575073242188, 0.0001609325408935547, 0.0001652240753173828, 0.0001544952392578125, 0.0001742839813232422, 0.00015854835510253906, 0.00015926361083984375, 0.00015854835510253906, 0.0001609325408935547, 0.00016069412231445312, 0.00016307830810546875, 0.00015592575073242188, 0.00015592575073242188, 0.00015783309936523438, 0.00017976760864257812, 0.00015854835510253906, 0.00015735626220703125, 0.00016188621520996094, 0.0001595020294189453, 0.000164031982421875]}
>>> Number of batch per epoch: 10010
Max Memory Allocated 0 Bytes
Tue Feb 21 21:41:18 CET 2023

Fichier d'erreur :

In [8]:
%cat {search_log(contains=jobid[0], with_err=True)['stderr'][0]}
Loading pytorch-gpu/py3/1.11.0
  Loading requirement: cuda/11.2 nccl/2.9.6-1-cuda cudnn/8.1.1.33-cuda gcc/8.4.1
    openmpi/4.1.1-cuda intel-mkl/2020.4 magma/2.5.4-cuda sox/14.4.2
    sparsehash/2.0.3 libjpeg-turbo/2.1.3
+ srun python -u dlojz.py -b 128 --image-size 176 --test
/gpfslocalsup/pub/anaconda-py3/2021.05/envs/pytorch-gpu-1.11.0+py3.9.12/lib/python3.9/site-packages/apex/pyprof/__init__.py:5: FutureWarning: pyprof will be removed by the end of June, 2022
  warnings.warn("pyprof will be removed by the end of June, 2022", FutureWarning)

real	6m27.615s
user	0m0.020s
sys	0m0.013s
+ date

Différence entre deux scripts¶

Pour le debug ou pour comparer son code avec les solutions mises à disposition, la fonction suivante permet d'afficher une page html contenant un différentiel de fichiers texte.

In [9]:
s1 = "dlojz.py"
s2 = "./solutions/dlojz1_3.py"
compare(s1, s2)

Voir le résultat du différentiel de fichiers sur la page suivante (attention au spoil !) :

compare.html


Dataset et modèle¶

Cette partie permet de visualiser les caractéristiques du dataset et du modèle utilisés.

Imagenet¶

Train set¶

In [10]:
import os
import torchvision
import torchvision.transforms as transforms
import torch
import numpy as np
import matplotlib.pyplot as plt

transform = transforms.Compose([ 
        transforms.RandomResizedCrop(176),             # Random resize - Data Augmentation
        transforms.RandomHorizontalFlip(),              # Horizontal Flip - Data Augmentation
        transforms.ToTensor(),                          # convert the PIL Image to a tensor
        transforms.Normalize(mean=(0.485, 0.456, 0.406),
                             std=(0.229, 0.224, 0.225))
        ])
    
    
train_dataset = torchvision.datasets.ImageNet(root=os.environ['ALL_CCFRSCRATCH']+'/imagenet',
                                                  transform=transform)
train_dataset
Out[10]:
Dataset ImageNet
    Number of datapoints: 1281167
    Root location: /gpfsscratch/idris/for/commun/imagenet
    Split: train
    StandardTransform
Transform: Compose(
               RandomResizedCrop(size=(176, 176), scale=(0.08, 1.0), ratio=(0.75, 1.3333), interpolation=bilinear, antialias=warn)
               RandomHorizontalFlip(p=0.5)
               ToTensor()
               Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))
           )
In [11]:
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,    
                                           batch_size=4,
                                           shuffle=True)
batch = next(iter(train_loader))
print('X train batch, shape: {}, data type: {}, Memory usage: {} bytes'
      .format(batch[0].shape, batch[0].dtype, batch[0].element_size()*batch[0].nelement()))
print('Y train batch, shape: {}, data type: {}, Memory usage: {} bytes'
      .format(batch[1].shape, batch[1].dtype, batch[1].element_size()*batch[1].nelement()))

img = batch[0][0].numpy().transpose((1,2,0))
plt.imshow(img)
plt.axis('off')
labels_cls, labels_id = torch.load(os.environ['ALL_CCFRSCRATCH']+'/imagenet/meta.bin')
label = labels_cls[np.unique(labels_id)[batch[1][0].numpy()]]
_ = plt.title('label class: {}'.format(label[0]))
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
X train batch, shape: torch.Size([4, 3, 176, 176]), data type: torch.float32, Memory usage: 1486848 bytes
Y train batch, shape: torch.Size([4]), data type: torch.int64, Memory usage: 32 bytes

Validation set¶

In [12]:
val_transform = transforms.Compose([
                                    transforms.Resize((256, 256)),
                                    transforms.CenterCrop(224),
                                    transforms.ToTensor(),   # convert the PIL Image to a tensor
                                    transforms.Normalize(mean=(0.485, 0.456, 0.406),
                                    std=(0.229, 0.224, 0.225))])

val_dataset = torchvision.datasets.ImageNet(root=os.environ['ALL_CCFRSCRATCH']+'/imagenet', split='val',
                        transform=val_transform)
val_dataset
Out[12]:
Dataset ImageNet
    Number of datapoints: 50000
    Root location: /gpfsscratch/idris/for/commun/imagenet
    Split: val
    StandardTransform
Transform: Compose(
               Resize(size=(256, 256), interpolation=bilinear, max_size=None, antialias=warn)
               CenterCrop(size=(224, 224))
               ToTensor()
               Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))
           )

Resnet-50¶

In [13]:
import torchvision.models as models
model = models.resnet50()
print('number of total parameters: {}'.format(sum([p.numel() for p in model.parameters()])))
print('number of trainable parameters: {}'.format(sum([p.numel() for p in model.parameters() if p.requires_grad])))
number of total parameters: 25557032
number of trainable parameters: 25557032

TP1_0 : Baseline CPU¶

Ce TP consiste à appliquer le code baseline pour prendre en main les fonctionnalités de test et découvrir le code.

TODO :

  1. Exécuter les cellules suivantes (le job prend 7 minutes environ)
  2. Puis, ouvrir le fichier dlojz.py

Remarque :

  • l'option test lance un apprentissage de 50 itérations.
  • les chronomètres mesurent les temps de la 10e à la 50e itération et restitue un temps moyen par itération.
  • les parties DON'T MODIFY dans le script ne doivent pas être modifiées.
In [14]:
n_gpu = 1
batch_size = 128
In [15]:
command = f'dlojz.py -b {batch_size} --image-size {image_size} --test'
command
Out[15]:
'dlojz.py -b 128 --image-size 176 --test'

Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.

Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.

In [16]:
jobid = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
                   account=account, time_max='00:20:00', constraint='v100-32g')
print(f'jobid = {jobid}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 10 cpus per task
Submitted batch job 247933
jobid = ['247933']

Copier-coller la sortie jobid = ['xxxxx'] dans la cellule suivante.

Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.

In [17]:
#jobid = ['1493206']
In [18]:
display_slurm_queue(name)
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
            247933   gpu_p13   pseudo  cfor132  R       6:31      1 r6i3n0

 Done!
In [19]:
controle_technique(jobid)
Train throughput: 26.77 images/second
GPU throughput: 26.77 images/second
epoch time: 47865.19 seconds
training time estimation for 90 epochs (with validations): 1222.78 hours
-----------
training step time average (fwd/bkwd on GPU): 4.781577 sec (39.5%/60.5%) +/- 0.110501
loading step time average (CPU to GPU): 0.000161 sec +/- 0.000015
-----------
ELIGIBLE to run 1 epochs

Le code baseline dlo-jz.py a été exécuté sur le CPU (contrairement à ce qui est indiqué par le contrôle technique) en mode test, soit sur 50 itérations.

Dans le prochain exercice nous verrons ensemble l'accélération sur 1 GPU.

Garage


TP1_1 : Accélération GPU¶

TODO : dans le script dlojz.py :

  • Définir la variable gpu et envoyer le modèle dans la mémoire du GPU.
# define model
    gpu = torch.device("cuda")
    model = models.resnet50()
    model = model.to(gpu)
  • Envoyer les batches d'images d'entrée et les labels associés sur le GPU, pour les étapes de training et de *validation. Remarque* : non_blocking=args.non_blocking sera utile plus tard lors de la définition d'un DataLoader distribué.
# distribution of images and labels to all GPUs
    images = images.to(gpu, non_blocking=args.non_blocking)
    labels = labels.to(gpu, non_blocking=args.non_blocking)

et

# distribution of images and labels to all GPUs
    val_images = val_images.to(gpu, non_blocking=args.non_blocking)
    val_labels = val_labels.to(gpu, non_blocking=args.non_blocking)
  • Envoyer les tenseurs pour les métriques de validation sur le GPU.
## Initialisation  
    if idr_torch.rank == 0: accuracies = []
    val_loss = torch.Tensor([0.]).to(gpu)                  # send to GPU
    val_accuracy = torch.Tensor([0.]).to(gpu)              # send to GPU
In [20]:
n_gpu = 1
batch_size = 128
command = f'dlojz.py -b {batch_size} --image-size {image_size} --test'
command
Out[20]:
'dlojz.py -b 128 --image-size 176 --test'

Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.

Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.

In [21]:
jobid = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
                   account=account, time_max='00:10:00', constraint='v100-32g')
print(f'jobid = {jobid}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 10 cpus per task
Submitted batch job 247940
jobid = ['247940']

Copier-coller la sortie jobid = ['xxxxx'] dans la cellule suivante.

Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.

In [22]:
#jobid = ['1493317']
In [23]:
display_slurm_queue(name)
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
            247940   gpu_p13   pseudo  cfor132  R       0:41      1 r6i3n0

 Done!
In [24]:
controle_technique(jobid)
Train throughput: 488.88 images/second
GPU throughput: 489.76 images/second
epoch time: 2620.86 seconds
training time estimation for 90 epochs (with validations): 67.99 hours
-----------
training step time average (fwd/bkwd on GPU): 0.261352 sec (32.8%/66.8%) +/- 0.001361
loading step time average (CPU to GPU): 0.000472 sec +/- 0.000974
-----------
ELIGIBLE to run 18 epochs

Test d'occupation mémoire¶

Afin de mesurer l'impact de la taille de batch sur l'occupation mémoire et sur le throughput, la cellule suivante permet de soumettre plusieurs jobs avec des tailles de batch croissantes. Dans les cas où la mémoire est saturée et dépasse la capacité du GPU, le système renverra une erreur CUDA Out of Memory.

Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.

Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.

In [25]:
n_gpu = 1
batch_size = [8, 16, 32, 64, 128, 256, 512, 1024]
command = [f'dlojz.py -b {b} --image-size {image_size} --test'
          for b in batch_size]
jobids = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
                   account=account, time_max='00:10:00', constraint='v100-32g')
print(f'jobids = {jobids}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 10 cpus per task
Submitted batch job 247942
Submitted batch job 247943
Submitted batch job 247944
Submitted batch job 247945
Submitted batch job 247946
Submitted batch job 247947
Submitted batch job 247948
Submitted batch job 247949
jobids = ['247942', '247943', '247944', '247945', '247946', '247947', '247948', '247949']

Copier-coller la sortie jobids = ['xxxxx', ...] dans la cellule suivante.

Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.

In [26]:
#jobids = ['1493424', '1493432', '1493433', '1493434', '1493435', '1493436', '1493438', '1493440']
In [27]:
display_slurm_queue(name)
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
            247948   gpu_p13   pseudo  cfor132  R       1:37      1 r6i7n6

 Done!
In [28]:
GPU_underthehood(jobids)
Batch size per GPU: 8 Max GPU Memory Allocated: 2.93 GB, Troughput: 233.749 images/second
Batch size per GPU: 16 Max GPU Memory Allocated: 2.93 GB, Troughput: 357.828 images/second
Batch size per GPU: 32 Max GPU Memory Allocated: 2.94 GB, Troughput: 396.994 images/second
Batch size per GPU: 64 Max GPU Memory Allocated: 3.55 GB, Troughput: 440.181 images/second
Batch size per GPU: 128 Max GPU Memory Allocated: 6.76 GB, Troughput: 487.584 images/second
Batch size per GPU: 256 Max GPU Memory Allocated: 13.20 GB, Troughput: 488.307 images/second
Batch size per GPU: 512 Max GPU Memory Allocated: 26.08 GB, Troughput: 485.815 images/second
Batch size per GPU: 1024 CUDA out of memory
Memory occupancy by Model part : 0.331 +/- 0.011 GB

Le dernier job a atteint le seuil CUDA Out Of Memory :

In [29]:
controle_technique([jobids[-1]])

Garage


TP1_2 : Automatic Mixed Precision¶

TODO : dans le script dlojz.py :

  • Importer les fonctionnalités liées à l'Automatic Mixed Precision.
from torch.cuda.amp import autocast, GradScaler
  • Initialiser le scaler.
# Creates a GradScaler once at the beginning of training.
    scaler = GradScaler()
  • Implémenter l'autocasting (le changement de précision, FP32 à FP16) dans le forward , avec la ligne with autocast(): dans la boucle de training et la boucle de validation.
...
    # Runs the forward pass with autocasting.
    with autocast():
        outputs = model(images)
        loss = criterion(outputs, labels)
    ...
  • Implémenter le gradient scaling pour la seule boucle de training. Note: À la place des lignes loss.backward() et optimizer.step().
# Scales loss.  Calls backward() on scaled loss to create scaled gradients.
    # Backward passes under autocast are not recommended.
    # Backward ops run in the same dtype autocast chose for corresponding forward ops.
    scaler.scale(loss).backward()

    # scaler.step() first unscales the gradients of the optimizer's assigned params.
    # If these gradients do not contain infs or NaNs, optimizer.step() is then called,
    # otherwise, optimizer.step() is skipped.
    scaler.step(optimizer)

    # Updates the scale for next iteration.
    scaler.update()
In [30]:
n_gpu = 1
batch_size = 128
command = f'dlojz.py -b {batch_size} --image-size {image_size} --test'
command
Out[30]:
'dlojz.py -b 128 --image-size 176 --test'

Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.

Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.

In [31]:
jobid = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
                    account=account, time_max='00:10:00', constraint='v100-32g')
print(f'jobid = {jobid}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 10 cpus per task
Submitted batch job 247952
jobid = ['247952']

Copier-coller la sortie jobid = ['xxxxx'] dans la cellule suivante.

Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.

In [32]:
#jobid = ['1493501']
In [33]:
display_slurm_queue(name)
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
            247952   gpu_p13   pseudo  cfor132  R       0:27      1 r6i3n0

 Done!
In [34]:
controle_technique(jobid)
Train throughput: 982.71 images/second
GPU throughput: 986.09 images/second
epoch time: 1303.83 seconds
training time estimation for 90 epochs (with validations): 34.35 hours
-----------
training step time average (fwd/bkwd on GPU): 0.129805 sec (37.0%/63.0%) +/- 0.004486
loading step time average (CPU to GPU): 0.000447 sec +/- 0.001149
-----------
ELIGIBLE to run 30 epochs

Test d'occupation mémoire¶

Afin de mesurer l'impact de la taille de batch sur l'occupation mémoire et sur le throughput, la cellule suivante permet de soumettre plusieurs jobs avec des tailles de batch croissantes. Dans les cas où la mémoire est saturée et dépasse la capacité du GPU, le système renverra une erreur CUDA Out of Memory.

Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.

Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.

In [35]:
n_gpu = 1
batch_size = [16, 32, 64, 128, 256, 512, 1024, 2048]
command = [f'dlojz.py -b {b} --image-size {image_size} --test'
          for b in batch_size]
jobids = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
                   account=account, time_max='00:10:00', constraint='v100-32g')
print(f'jobids = {jobids}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 10 cpus per task
Submitted batch job 247953
Submitted batch job 247954
Submitted batch job 247955
Submitted batch job 247956
Submitted batch job 247957
Submitted batch job 247958
Submitted batch job 247959
Submitted batch job 247960
jobids = ['247953', '247954', '247955', '247956', '247957', '247958', '247959', '247960']

Copier-coller la sortie jobids = ['xxxxx', ...] dans la cellule suivante.

Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.

In [36]:
#jobids = ['1493508', '1493509', '1493511', '1493512', '1493513', '1493515', '1493516', '1493517']
In [37]:
display_slurm_queue(name)
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
            247959   gpu_p13   pseudo  cfor132  R       1:41      1 r6i7n6

 Done!
In [38]:
GPU_underthehood(jobids)
Batch size per GPU: 16 Max GPU Memory Allocated: 1.78 GB, Troughput: 370.434 images/second
Batch size per GPU: 32 Max GPU Memory Allocated: 1.79 GB, Troughput: 646.580 images/second
Batch size per GPU: 64 Max GPU Memory Allocated: 2.01 GB, Troughput: 883.572 images/second
Batch size per GPU: 128 Max GPU Memory Allocated: 3.68 GB, Troughput: 988.727 images/second
Batch size per GPU: 256 Max GPU Memory Allocated: 7.00 GB, Troughput: 1041.105 images/second
Batch size per GPU: 512 Max GPU Memory Allocated: 13.66 GB, Troughput: 1067.721 images/second
Batch size per GPU: 1024 Max GPU Memory Allocated: 26.95 GB, Troughput: 999.733 images/second
Batch size per GPU: 2048 CUDA out of memory
Memory occupancy by Model part : 0.353 +/- 0.015 GB

Changement de taille de batch¶

TODO : Choisir pour la suite du TP une taille de batch par GPU qui vous semble la plus pertinente selon le test précédent.

In [39]:
## Choisir un batch size optimal
bs_optim = 512
In [40]:
n_gpu = 1
command = f'dlojz.py -b {bs_optim} --image-size {image_size} --test'
command
Out[40]:
'dlojz.py -b 512 --image-size 176 --test'

Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.

Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.

In [41]:
jobid = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
                    account=account, time_max='00:10:00', constraint='v100-32g')
print(f'jobid = {jobid}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 10 cpus per task
Submitted batch job 247963
jobid = ['247963']

Copier-coller la sortie jobids = ['xxxxx', ...] dans la cellule suivante.

Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.

In [42]:
#jobid = ['1493563']
In [43]:
display_slurm_queue(name)
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
            247963   gpu_p13   pseudo  cfor132  R       0:59      1 r6i3n0

 Done!
In [44]:
controle_technique(jobid)
Train throughput: 1059.72 images/second
GPU throughput: 1060.72 images/second
epoch time: 1209.31 seconds
training time estimation for 90 epochs (with validations): 32.26 hours
-----------
training step time average (fwd/bkwd on GPU): 0.482691 sec (40.2%/64.2%) +/- 0.065941
loading step time average (CPU to GPU): 0.000454 sec +/- 0.000481
-----------
ELIGIBLE to run 31 epochs

Commentaires


TP1_3 : Channels Last Memory Format¶

TODO : dans le script dlojz.py :

  • Lors de l'envoie du modèle au GPU, configurer le paramètre memory_format avec torch.channels_last.
# define model
    gpu = torch.device("cuda")
    model = models.resnet50()
    model = model.to(gpu, memory_format=torch.channels_last)
  • Lors de l'envoie des images d'entrée au GPU, configurer le paramètre memory_format avec torch.channels_last.
# distribution of images and labels to all GPUs
    images = images.to(gpu, non_blocking=args.non_blocking, memory_format=torch.channels_last)
    labels = labels.to(gpu, non_blocking=args.non_blocking)

et

# distribution of images and labels to all GPUs
    val_images = val_images.to(gpu, non_blocking=args.non_blocking, memory_format=torch.channels_last)
    val_labels = val_labels.to(gpu, non_blocking=args.non_blocking)
In [45]:
n_gpu = 1
command = f'dlojz.py -b {bs_optim} --image-size {image_size} --test'
command
Out[45]:
'dlojz.py -b 512 --image-size 176 --test'

Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.

Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.

In [46]:
jobid = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
                    account=account, time_max='00:10:00', constraint='v100-32g')
print(f'jobid = {jobid}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 10 cpus per task
Submitted batch job 247966
jobid = ['247966']

Copier-coller la sortie jobids = ['xxxxx', ...] dans la cellule suivante.

Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.

In [47]:
#jobid = ['1493619']
In [48]:
display_slurm_queue(name)
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
            247966   gpu_p13   pseudo  cfor132  R       0:52      1 r6i3n0

 Done!
In [49]:
controle_technique(jobid)
Train throughput: 1709.45 images/second
GPU throughput: 1711.67 images/second
epoch time: 749.68 seconds
training time estimation for 90 epochs (with validations): 21.49 hours
-----------
training step time average (fwd/bkwd on GPU): 0.299123 sec (43.0%/64.9%) +/- 0.074310
loading step time average (CPU to GPU): 0.000389 sec +/- 0.000138
-----------
ELIGIBLE to run 41 epochs

Test d'occupation mémoire¶

Afin de mesurer l'impact de la taille de batch sur l'occupation mémoire et sur le throughput, la cellule suivante permet de soumettre plusieurs jobs avec des tailles de batch croissantes. Dans les cas où la mémoire est saturée et dépasse la capacité du GPU, le système renverra une erreur CUDA Out of Memory.

Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.

Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.

In [50]:
n_gpu = 1
batch_size = [16, 32, 64, 128, 256, 512, 1024, 2048]
command = [f'dlojz.py -b {b} --image-size {image_size} --test'
          for b in batch_size]
jobids = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
                   account=account, time_max='00:10:00', constraint='v100-32g')
print(f'jobids = {jobids}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 10 cpus per task
Submitted batch job 247968
Submitted batch job 247969
Submitted batch job 247970
Submitted batch job 247971
Submitted batch job 247972
Submitted batch job 247973
Submitted batch job 247974
Submitted batch job 247975
jobids = ['247968', '247969', '247970', '247971', '247972', '247973', '247974', '247975']

Copier-coller la sortie jobids = ['xxxxx', ...] dans la cellule suivante.

Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.

In [51]:
#jobids = ['1493654', '1493656', '1493657', '1493658', '1493659', '1493660', '1493661', '1493662']
In [52]:
display_slurm_queue(name)
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
            247975   gpu_p13   pseudo  cfor132  R       1:17      1 r6i3n3

 Done!
In [53]:
GPU_underthehood(jobids)
Batch size per GPU: 16 Max GPU Memory Allocated: 1.69 GB, Troughput: 377.952 images/second
Batch size per GPU: 32 Max GPU Memory Allocated: 1.70 GB, Troughput: 712.878 images/second
Batch size per GPU: 64 Max GPU Memory Allocated: 2.02 GB, Troughput: 1170.408 images/second
Batch size per GPU: 128 Max GPU Memory Allocated: 3.69 GB, Troughput: 1442.422 images/second
Batch size per GPU: 256 Max GPU Memory Allocated: 7.01 GB, Troughput: 1582.987 images/second
Batch size per GPU: 512 Max GPU Memory Allocated: 13.67 GB, Troughput: 1717.691 images/second
Batch size per GPU: 1024 Max GPU Memory Allocated: 26.96 GB, Troughput: 1592.242 images/second
Batch size per GPU: 2048 CUDA out of memory
Memory occupancy by Model part : 0.365 +/- 0.015 GB

Garage